package controleur;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

import modele.ModeleClasse;
import modele.ModeleRelation;
import modele.Relations;

import outils.Operation;
import outils.PaireClasse;
import outils.TypesOperations;
import outils.Divers;

import vue.VueAjoutClasse;
import vue.VueFenetre;
import vue.VuePanelClasse;
import vue.VueToile;


public class ControleurPanelClasse implements ActionListener {

	private VueAjoutClasse vac;
	private VuePanelClasse vpc;
	private VueFenetre vf;
	private VueToile vt;
	private MousePanelClasse m;
	private MouseMotionPanelClasse mm;
	private int xClassePrec;
	private int yClassePrec;
	private JFrame fenCards;
	private JComboBox card1, card2;
	private JLabel texteCard1, texteCard2;
	private PaireClasse aModifier;
	private boolean dragged;
	private boolean canBeDragged;
	
	public static VuePanelClasse classe1 = null;
	public static VuePanelClasse classe2 = null;
	public static VuePanelClasse classeSelectionnee = null;
	
	
	public ControleurPanelClasse(VuePanelClasse p, VueFenetre f, VueToile t) {
		this.vpc = p;
		this.vf = f;
		this.m = new MousePanelClasse();
		this.mm = new MouseMotionPanelClasse();
		this.vpc.addMouseListener(m);
		this.vpc.addMouseMotionListener(mm);
		this.vt = t;
		this.aModifier = null;
		this.dragged = false;
		this.canBeDragged = false;
		
		initFenCards();
	}
	
	public void initFenCards() {
		/* Initialisation de la fenêtre pour éditer les cardinalités */
		this.fenCards = new JFrame("Cardinalités");
		fenCards.setPreferredSize(new Dimension(400, 180));
		
		card1 = new JComboBox(Divers.cardinalites);
		card2 = new JComboBox(Divers.cardinalites);
		card1.setPreferredSize(new Dimension(100, 20));
		card2.setPreferredSize(new Dimension(100, 20));
		
		texteCard1 = new JLabel();
		texteCard2 = new JLabel();
		texteCard1.setPreferredSize(new Dimension(250, 20));
		texteCard2.setPreferredSize(new Dimension(250, 20));
		
		JButton btnOk = new JButton("Ok");
		btnOk.setPreferredSize(new Dimension(100, 30));
		JButton btnAnnuler = new JButton("Annuler");
		btnAnnuler.setPreferredSize(new Dimension(100, 30));
		btnOk.setActionCommand("OkCards");
		btnAnnuler.setActionCommand("AnnulerCards");
		btnOk.addActionListener(new ControleurBoutons());
		btnAnnuler.addActionListener(new ControleurBoutons());
		
		fenCards.setResizable(false);
		fenCards.setLayout(null);
		
		fenCards.add(texteCard1, null);
		fenCards.add(card1, null);
		fenCards.add(texteCard2, null);
		fenCards.add(card2, null);
		fenCards.add(btnOk, null);
		fenCards.add(btnAnnuler, null);
		
		Insets insets = fenCards.getInsets();
        Dimension size = texteCard1.getPreferredSize();
        texteCard1.setBounds(30 + insets.left, 60 + insets.top, size.width, size.height);
        size = texteCard2.getPreferredSize();
        texteCard2.setBounds(30 + insets.left, 110 + insets.top, size.width, size.height);
        
        size = card1.getPreferredSize();
        card1.setBounds(270 + insets.left, 60 + insets.top, size.width, size.height);
        size = card2.getPreferredSize();
        card2.setBounds(270 + insets.left, 110 + insets.top, size.width, size.height);
        
        size = btnOk.getPreferredSize();
        btnOk.setBounds(50 + insets.left, 180 + insets.top, size.width, size.height);
        size = btnAnnuler.getPreferredSize();
        btnAnnuler.setBounds(230 + insets.left, 180 + insets.top, size.width, size.height);
		
		fenCards.setPreferredSize(new Dimension(400, 300));
	}
	
	public class MousePanelClasse implements MouseListener {

		public void mouseClicked(MouseEvent e) {
			
			if(ControleurToile.dessinerRelation == 1) {				
				classe1 = (VuePanelClasse) e.getSource();
				ControleurToile.dessinerRelation = 2;
			}
			else if(ControleurToile.dessinerRelation == 2) {
				classe2 = (VuePanelClasse) e.getSource();
				ControleurToile.dessinerRelation = 0;
				vt.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
				
				if(!relationExisteDeja() && classe1 != classe2) {
					if(ControleurToile.rel == Relations.ASSOCIATION) {
						texteCard1.setText("Cardinalité de : " + classe1.getModele().getNom());
						texteCard1.setVisible(true);
						texteCard2.setText("Cardinalité de : " + classe2.getModele().getNom());
						card1.setSelectedIndex(0);
						card2.setSelectedIndex(0);
						
						// on lance la fenêtre d'édition des cardinalités de l'association
						fenCards.pack();
						fenCards.setVisible(true);
					}
					else {
						PaireClasse pc = new PaireClasse(classe1, null, classe2, null, ControleurToile.rel);
						vt.nouvelleRelation(pc);
						
						// Ajout dans la pileUndo
						ControleurToile.pileUndo.empiler(new Operation(pc, TypesOperations.AJOUT_REL, false));
						nouvelleOperation();
						
						vf.repaint();
						vf.pack();
						
						classe1 = null;
						classe2 = null;
						ControleurToile.rel = null;
					}
				}
			}
			else if(e.getButton() == MouseEvent.BUTTON3) {
				JPopupMenu jpm = new JPopupMenu();
				
				// Si la classe a des relations, proposer de les modifier ou supprimer
				Vector<PaireClasse> listeRelations = vt.getListeRelations();
				for(PaireClasse pc : listeRelations) {
					String nomClasse = "";
					
					if(pc.getVpc1() == vpc || pc.getVpc2() == vpc) {
						if(pc.getVpc1() == vpc) {
							nomClasse = pc.getVpc2().getModele().getNom();
						}
						else if(pc.getVpc2() == vpc) {
							nomClasse = pc.getVpc1().getModele().getNom();
						}
						
						if(pc.getTypeRelations() == Relations.ASSOCIATION) {
							JMenuItem modifierCards = new JMenuItem("Modifier l'association avec " + nomClasse);
							JMenuItem supprimerAssoc = new JMenuItem("Supprimer l'association avec " + nomClasse);
							supprimerAssoc.setActionCommand("SupprimerRelation_" + nomClasse);
							supprimerAssoc.addActionListener(ControleurPanelClasse.this);
							modifierCards.setActionCommand("ModifierCards_" + nomClasse);
							modifierCards.addActionListener(ControleurPanelClasse.this);
							jpm.add(modifierCards);
							jpm.add(supprimerAssoc);
						}
						else {
							JMenuItem supprimerAssoc = new JMenuItem();
							if(pc.getTypeRelations() == Relations.COMPOSITION) {
								supprimerAssoc.setText("Supprimer la composition avec " + nomClasse);
							}
							else if(pc.getTypeRelations() == Relations.AGREGATION) {
								supprimerAssoc.setText("Supprimer l'agrégation avec " + nomClasse);
							}
							supprimerAssoc.setActionCommand("SupprimerRelation_" + nomClasse);
							supprimerAssoc.addActionListener(ControleurPanelClasse.this);
							jpm.add(supprimerAssoc);
						}
						
						jpm.addSeparator();
					}
				}
				
				JMenuItem modifier = new JMenuItem("Modifier");
				JMenuItem supprimer = new JMenuItem("Supprimer");
				JMenuItem copier = new JMenuItem("Copier");
				JMenuItem couper = new JMenuItem("Couper");
				supprimer.setActionCommand("SupprimerClasse");
				supprimer.addActionListener(ControleurPanelClasse.this);
				modifier.setActionCommand("ModifierClasse");
				modifier.addActionListener(ControleurPanelClasse.this);
				copier.setActionCommand("CopierClasse");
				copier.addActionListener(ControleurPanelClasse.this);
				couper.setActionCommand("CouperClasse");
				couper.addActionListener(ControleurPanelClasse.this);
				
				jpm.add(copier);
				jpm.add(couper);
				jpm.addSeparator();
				jpm.add(modifier);
				jpm.add(supprimer);
				
				jpm.setVisible(true);
				jpm.show(vpc, e.getX(), e.getY());
			}
			else {
				classeSelectionnee = vpc;
				vpc.setBorder(BorderFactory.createLineBorder(Color.BLUE, 2));
				
				vf.setCopyAndCutBtnEnabled(true);
			}
		}

		public void mouseEntered(MouseEvent e) {}

		public void mouseExited(MouseEvent e) {}

		public void mousePressed(MouseEvent e) {
			if(e.getButton() == MouseEvent.BUTTON1 && ControleurToile.dessinerRelation < 1) {
				VuePanelClasse vpc = (VuePanelClasse) e.getSource();
				vpc.xPrec = e.getX();
				vpc.yPrec = e.getY();
				xClassePrec = vpc.getX();
				yClassePrec = vpc.getY();
				canBeDragged = true;
			}
			else {
				canBeDragged = false;
			}
		}

		public void mouseReleased(MouseEvent e) {
			if(dragged) {
				VuePanelClasse vpc = (VuePanelClasse) e.getSource();
				ControleurToile.pileUndo.empiler(new Operation(vpc, TypesOperations.DEPLACEMENT, 
								xClassePrec, yClassePrec, false));
				nouvelleOperation();
		        
		        vpc.getModele().setPosition(vpc.getX(), vpc.getY());
		        
		        dragged = false;
			}
		}
	}
	
	public void redimensionnerToile(VuePanelClasse vpc, MouseEvent e){
        boolean changed = false;
        int W = vpc.getWidth();
        int H = vpc.getHeight();

        int x = vpc.getX();
        int y = vpc.getY();
        if (x < 0) x = 0;
        if (y < 0) y = 0;
        Rectangle rect = new Rectangle(x, y, W, H);

        vpc.setLocation(x, y);
        vt.scrollRectToVisible(rect);
        
        int this_width = (x + W + 2);
        if (this_width > vt.area.width) {
            vt.area.width = this_width; changed=true;
        }
        int this_height = (y + H + 2);
        if (this_height > vt.area.height) {
            vt.area.height = this_height; changed=true;
        }

        if (changed) {
	        vt.setPreferredSize(vt.area);
	        vt.revalidate();
        }
        
        vt.repaint();
    }

	public class MouseMotionPanelClasse implements MouseMotionListener{

		public void mouseDragged(MouseEvent e) {
			if(canBeDragged) {
				dragged = true;
			
				VuePanelClasse vpc = (VuePanelClasse) e.getSource();
				
				int x = vpc.getX() + (e.getX()-vpc.xPrec);
				int y = vpc.getY() + (e.getY()-vpc.yPrec);
				vpc.setLocation(x, y);
				
				redimensionnerToile(vpc, e);
							
				vf.repaint();
				vf.pack();
			}
		}

		public void mouseMoved(MouseEvent e) {}
	}
	
	public void actionPerformed(ActionEvent e) {
		String etiquette = e.getActionCommand();
		String[] etiquettes = etiquette.split("_");
		String first = "";
		String second = "";
		
		if(etiquettes.length > 1) {
			first = etiquettes[0];
			second = etiquettes[1];
		}
		
		if(etiquette.equals("ModifierClasse")) {
			this.vac = new VueAjoutClasse(this, vpc.getModele(), vt);
		}
		
		else if(etiquette.equals("ValiderModifier")) {
			/* On vérifie si le nom de la classe existe déjà */
			if(!vac.getAncienNom().equals(vac.getTxtnc().getText()) &&
					Divers.nomDeClasseExisteDeja(vt, vac.getTxtnc().getText())) {
				JOptionPane.showMessageDialog(vf, "Ce nom de classe est déjà utilisé !");
			}
			else {
				this.vac.setVisible(false);				
	
				/* On ajoute un modèle correspondant à la classe dans le modéle de la toile */			
				ModeleClasse mc = Divers.getModeleClasseOfVac(this.vac);
				vt.getModele().supprimer(vpc.getModele()); // on supprime d'abord l'ancien modèle
				vt.getModele().ajouter(mc); // puis on ajoute le nouveau modèle
				
				/* On ajoute la modification de la classe à la pile des opérations */
		        ControleurToile.pileUndo.empiler(new Operation(vpc, TypesOperations.MODIF_CLASSE, 
		        		vpc.getModele(), mc, false));
		        nouvelleOperation();
		        
				/* On modifie le modèle du vpc */
				vpc.setModele(mc);
		        
				/* Enfin, on redessine la fenêtre */
				vf.pack();
				vf.repaint();
			}
		}
		
		else if(etiquette.equals("SupprimerClasse")) {
			supprimerClasse();
		}
		
		else if(etiquette.equals("CopierClasse")) {
			ControleurToile.classeAcopier = vpc;
			vf.setPasteBtnEnabled(true);
		}
		
		else if(etiquette.equals("CouperClasse")) {
			ControleurToile.classeAcopier = vpc;
			supprimerClasse();
			vf.setCopyAndCutBtnEnabled(false);
			vf.setPasteBtnEnabled(true);
		}
		
		else if(first.equals("ModifierCards")) {
			Vector<PaireClasse> listeRelations = vt.getListeRelations();
	        
			for(PaireClasse pc : listeRelations) {
				if( (pc.getVpc1() == vpc && pc.getVpc2().getModele().getNom().equals(second) && pc.getTypeRelations() == Relations.ASSOCIATION) ||
					(pc.getVpc2() == vpc && pc.getVpc1().getModele().getNom().equals(second) && pc.getTypeRelations() == Relations.ASSOCIATION)	) {
					aModifier = new PaireClasse(pc);
				}
			}
			
			card1.setSelectedItem(aModifier.getCard1());
			card2.setSelectedItem(aModifier.getCard2());
			
			if(aModifier.getVpc1() == vpc) {
				texteCard1.setText("Cardinalité de : "+vpc.getModele().getNom());
				texteCard2.setText("Cardinalité de : "+second);
			}
			else {
				texteCard1.setText("Cardinalité de : "+second);
				texteCard2.setText("Cardinalité de : "+vpc.getModele().getNom());
			}
			
			fenCards.pack();
			fenCards.setVisible(true);
		}
		
		else if(first.equals("SupprimerRelation")) {
			System.out.println("On va supprimer la relation avec " + second);
			Vector<PaireClasse> listeRelations = vt.getListeRelations();
	        PaireClasse aSupprimer = new PaireClasse();
			for(PaireClasse pc : listeRelations) {
				if( (pc.getVpc1() == vpc && pc.getVpc2().getModele().getNom().equals(second)) ||
					(pc.getVpc2() == vpc && pc.getVpc1().getModele().getNom().equals(second))	) {
					aSupprimer = pc;
				}
			}
			
			// Ajout de l'opération suppression de la relation dans la pileUndo
			ControleurToile.pileUndo.empiler(new Operation(aSupprimer, TypesOperations.SUPP_REL, false));
			nouvelleOperation();
	        
	        vt.enleverRelation(aSupprimer);
	        
	        vf.repaint();
			vf.pack();
		}
	}
	
	public boolean relationExisteDeja() {
		// si il ya déja une relation entre les 2 classes empêcher l'ajout
		Vector<PaireClasse> listeRelations = vt.getListeRelations();
	    boolean existeDeja = false;
		for(PaireClasse pc : listeRelations) {
			if( (pc.getVpc1() == classe1 && pc.getVpc2() == classe2) || 
					(pc.getVpc1() == classe2 && pc.getVpc2() == classe1) ) {
				existeDeja = true;
			}
		}
		return existeDeja;
	}
	
	public void supprimerClasse() {
		Operation op = new Operation(vpc, TypesOperations.SUPP_CLASSE, false);
		ControleurToile.pileUndo.empiler(op);
		nouvelleOperation();
        
		if(classeSelectionnee != null && classeSelectionnee == vpc) {
			classeSelectionnee = null;
			vf.setCopyAndCutBtnEnabled(false);
		}
		
        // on récupére les relations de cette classe
        Vector<PaireClasse> listeRelations = vt.getListeRelations();
        PaireClasse[] aSupprimer = new PaireClasse[listeRelations.size()];
        int nb = 0;
		for(PaireClasse pc : listeRelations) {
			if(pc.getVpc1() == vpc || pc.getVpc2() == vpc) {
				aSupprimer[nb] = pc;
				nb++;
			}
		}
		
		// si il ya des relations, on va aussi les supprimer, les opérations dans
		// la pileUndo vont devoir s'annuler en s'enchainant	        
		if(nb > 0) {				
			ControleurToile.pileUndo.getElementEnTete().setContinuerRedo(true);
        	for(int i = 0 ; i < nb ; i++) {
				// Ajout de l'opération suppression de la relation dans la pileUndo
				op = new Operation(aSupprimer[i], TypesOperations.SUPP_REL, false);
				op.setContinuerUndo(true);
				op.setContinuerRedo(true);
				ControleurToile.pileUndo.empiler(op);
		        vt.enleverRelation(aSupprimer[i]);
			}
			ControleurToile.pileUndo.getElementEnTete().setContinuerRedo(false);
        }
        
		// on enléve la classe de la vue
        vt.remove(vpc);
        if(vt.getComponentCount() == 0)
        	vf.setGenerateBtnEnabled(false);
		vf.repaint();
		
		// on supprime la classe du modéle
		vt.getModele().supprimer(vpc.getModele());
	}
	
	private class ControleurBoutons implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			String etiquette = e.getActionCommand();
			
			if(etiquette.equals("OkCards")) {
				// récupération des cardinalités
				if(card1.getSelectedItem().equals(Divers.cardinalites[0]) || 
				   card2.getSelectedItem().equals(Divers.cardinalites[0]) ) {
					JOptionPane.showMessageDialog(vf, "Vous n'avez pas choisi les cardinalités !");
				}
				else {
					// Modification d'une association
					if(aModifier != null) {						
						// Ajout de la modification de l'association dans la pileUndo
						ControleurToile.pileUndo.empiler(new Operation(
								new PaireClasse(aModifier), TypesOperations.MODIF_REL, false));
						nouvelleOperation();
				        
				        vt.enleverRelation(aModifier);
						aModifier.setCard1(card1.getSelectedItem().toString());
						aModifier.setCard2(card2.getSelectedItem().toString());
						vt.nouvelleRelation(aModifier);
						
				        vf.repaint();
						vf.pack();
				        
						aModifier = null;
					}
					// Ajout d'une association
					else {
						String c1 = card1.getSelectedItem().toString();
						String c2 = card2.getSelectedItem().toString();
						
						// Ajout de l'association dans la vue
						PaireClasse pc = new PaireClasse(classe1, c1, classe2, c2, Relations.ASSOCIATION);
						vt.nouvelleRelation(pc);
						
						// Ajouter l'association dans le modéle
						classe1.getModele().addRelation(new ModeleRelation(Relations.ASSOCIATION,
							classe1.getModele(), classe2.getModele(), Divers.stringToCard(c1), Divers.stringToCard(c2)), true);
						
						// Ajout de l'association dans la pileUndo
						ControleurToile.pileUndo.empiler(new Operation(pc, TypesOperations.AJOUT_REL, false));
						nouvelleOperation();
						
						vf.repaint();
						vf.pack();
						
						classe1 = null;
						classe2 = null;
						ControleurToile.rel = null;
					}
					
					fenCards.setVisible(false);
				}
			}
			
			else if(etiquette.equals("AnnulerCards")) {
				fenCards.setVisible(false);
			}
		}
		
	}
	
	public void nouvelleOperation() {
		ControleurToile.pileRedo.vider();
		vf.setUndoBtnEnabled(true);
        vf.setRedoBtnEnabled(false);
        vf.setSauvegarderBtnEnabled(true);
        vf.setTitle("*" + ControleurToile.nomToile + " - " + ControleurToile.nomProg);
	}

}
